package com.cqkjxy.commons.validations;


import com.cqkjxy.commons.entities.AbstractEntity;
import com.cqkjxy.commons.validations.annotations.UniqueColumn;
import com.cqkjxy.commons.validations.annotations.UniqueColumns;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;

@Component
public class UniqueColumnsValidator implements ConstraintValidator<UniqueColumns, AbstractEntity> {

    protected static final transient Logger log = LoggerFactory.getLogger(UniqueColumnsValidator.class);

    @PersistenceContext
    protected EntityManager entityManager;
    private UniqueColumn[] uniqueColumns;

    @Override
    public void initialize(UniqueColumns annotation) {
        this.uniqueColumns = annotation.value();
    }

    @Override
    public boolean isValid(AbstractEntity entity, ConstraintValidatorContext context) {
        assert entity != null;
        assert entityManager != null;

        BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(entity);

        if (uniqueColumns != null && uniqueColumns.length > 0) {
            for (UniqueColumn uc : uniqueColumns) {
                List<AbstractEntity> entities = null;
                try {
                    if (!StringUtils.isBlank(uc.field())) {
                        Object propertyValue =  wrapper.getPropertyValue(uc.field());
                        if (propertyValue == null) {
                            continue;
                        }

                        entities = (List<AbstractEntity>) entityManager.createQuery(uc.jpql())
                                .setParameter(1, propertyValue).getResultList();
                    } else if (uc.fields().length > 0) {
                        String[] fields = uc.fields();
                        Query query = entityManager.createQuery(uc.jpql());
                        for (int i = 0; i < fields.length; i++) {
                            Object propertyValue =  wrapper.getPropertyValue(fields[i]);
                            if (propertyValue == null) {
                                return true;
                            }

                            query.setParameter(i + 1, propertyValue);
                        }
                        entities = (List<AbstractEntity>) query.getResultList();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("Can not get entity property", e);
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate(uc.message()).addConstraintViolation();
                    return false;
                }
                if (entities == null || entities.size() == 0) {
                    continue;
                }

                if (entity.getPkId() == null) {
                    context.disableDefaultConstraintViolation();
                    context.buildConstraintViolationWithTemplate(uc.message()).addConstraintViolation();
                    return false;
                } else {
                    for (AbstractEntity entityInDb : entities) {
                        if (entity.getPkId().longValue() != entityInDb.getPkId().longValue()) {
                            context.disableDefaultConstraintViolation();
                            context.buildConstraintViolationWithTemplate(uc.message()).addConstraintViolation();
                            return false;
                        }
                    }
                }
            }
        }

        return true;
    }
}
